import time
from abc import ABC, abstractmethod

from loguru._logger import Logger

from fromework.base.enums import SelectType
from fromework.config.settings import DEFAULT_FREQUENCY
from fromework.utils.webdriver_factory import WebDriverFactory
from selenium.webdriver.remote.webdriver import WebElement
from typing import Tuple, Union
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.select import Select
from selenium.webdriver.support.wait import WebDriverWait
from fromework.utils.generator import NameGenerator
from fromework.utils.log import LogFactory


class BasePage(ABC):
    url: str = ''
    logger: Logger = None

    def __init__(self, url: str = ''):
        self.driver = WebDriverFactory.get_instance()
        self.url = url
        self.logger = LogFactory.get_log()

    @abstractmethod
    def is_load_successfully(self) -> bool:
        ...

    def visit_page(self) -> None:
        self.driver.get(self.url)

    def get_element(self, locator: Tuple) -> WebElement:
        return self.driver.find_element(*locator)

    def click(self, locator: Tuple) -> None:
        element: WebElement = self.get_element(locator)
        element.click()

    def double_click(self, locator: Tuple) -> None:
        element: WebElement = self.get_element(locator)
        action_chains = ActionChains(self.driver)
        action_chains.double_click(element)
        action_chains.perform()

    def right_click(self, locator: Tuple) -> None:
        element: WebElement = self.get_element(locator)
        action_chains = ActionChains(self.driver)
        action_chains.context_click(element)
        action_chains.perform()

    def hold_clock_until_timeout(self, locator: Tuple, timeout: float) -> None:
        element: WebElement = self.get_element(locator)
        action_chains = ActionChains(self.driver)
        action_chains.click_and_hold(element)
        self.force_wait(timeout)
        action_chains.release(element)

    def input_value(self, locator: Tuple, value: str) -> None:
        element: WebElement = self.get_element(locator)
        element.send_keys(value)

    def input_value_with_clean(self, locator: Tuple, value: str) -> None:
        element: WebElement = self.get_element(locator)
        element.clear()
        element.send_keys(value)

    def select_option(self, locator: Tuple, select_type: SelectType, value: Union[int, str]):
        element: WebElement = self.get_element(locator)
        select = Select(element)
        if select_type == SelectType.INDEX:
            select.select_by_index(value)
        elif select_type == SelectType.VALUE:
            select.select_by_value(value)
        elif select_type == SelectType.TEXT:
            select.select_by_visible_text(value)

    def get_display_text(self, locator: Tuple) -> str:
        element: WebElement = self.get_element(locator)
        return element.text

    def get_attr_value(self, locator: Tuple, attribute_name: str):
        element: WebElement = self.get_element(locator)
        return element.get_attribute(attribute_name)

    def handler_alter(self, accept: bool = True, value: str = None) -> None:
        if value:
            self.driver.switch_to.alert.send_keys(value)
        if accept:
            self.driver.switch_to.alert.accept()
        else:
            self.driver.switch_to.alert.dismiss()

    def move_to(self, x, y) -> None:
        script = f'window.scrollTo({x},{y})'
        self.driver.execute_script(script)

    def force_wait(self, timeout: float) -> None:
        time.sleep(timeout)

    def explicit_waiting(self, timeout: float, fullback, until_not: bool = False,
                         poll_frequency: float = DEFAULT_FREQUENCY, failed_message: str = ''):
        web_driver_wait = WebDriverWait(self.driver, timeout, poll_frequency=poll_frequency)
        if until_not:
            web_driver_wait.until_not(fullback, failed_message)
        else:
            web_driver_wait.until(fullback, failed_message)

    def take_screenshot(self):
        self.driver.get_screenshot_as_file(NameGenerator.generator_screenshot_name())
